<?php

declare( strict_types = 1 );

namespace App\Common\Model\Base\DataManager;

use App\Common as com;
use App\Common\Model\Base as nameModelBase;
use App\Common\Model\Base\DataManager\MainV3 as nameMainV3;

/**
 * Class DataManager
 * @property $dataStore
 * @method \Hyperf\Database\Model\Builder goModelQuery()
 * @DEL_method com\Model\Base\DataUnit\DataOperates goDatas()
 */
class MainV3 extends nameModelBase\DataUnit {

	/**
	 * 是否已注入Parent
	 * @var bool [false]默认
	 */
	private bool $isInjectParent = false;

	/**
	 * 保存时是否执行InjectParent保存操作
	 * @var bool
	 */
	private bool $isInjectParentIsSave = true;

	/**
	 * 关联父表的主数据id
	 * @var int|null
	 */
	protected ?int $mainDataIdOfParent = null;

	/**
	 * 关联子表的主数据id
	 * @var int|null
	 */
	protected ?int $mainDataIdOfClient = null;


	/**
	 * 关联父表数据的字段映射，格式[ '当前表字段', '父表字段' ]
	 * @var array|null [null]无
	 */
	protected array|null $relevanceDataFieldMapOfParent = [
		'id',
		null
	];

	/**
	 * 关联子表数据的字段映射，格式[ '当前表字段', '子表字段' ]
	 * @var array
	 */
	protected array $relevanceDataFieldMapOfClient = [
		'id',
		null
	];

	/**
	 * 父主id字段名
	 * @var string|null
	 */
	// protected ?string $parentMainDataFieldName = 'id';

	// protected string $clientMainDataFieldName = 'xxx_id'; // 子表主id字段名

	/**
	 * 主id字段名
	 * @var string
	 */
	// protected string $mainDataFieldName = 'id';

	/**
	 * client主id字段名
	 * @var string
	 */
	protected string $clientMainDataFieldName;

	/**
	 * 强制save模式
	 * @var bool|null [true]强制为create [false]强制为update [null]未强制
	 */
	protected ?bool $forceSaveMode = null;


	/**
	 * 保存成功后重置数据
	 * @var bool
	 */
	private bool $saveFinallyResetDatas = false;

	/**
	 * 是否为创建数据模式
	 * @return bool
	 */
	public function isCreate():bool {
		return $this->forceSaveMode ?? empty( $this->goModelQuery()->getQuery()->wheres );
	}

	/**
	 * 是否为更新数据模式
	 * @return bool
	 */
	public function isUpdate():bool {
		return ! $this->isCreate();
	}

	/**
	 * 是否存在更新数据
	 * @return bool
	 * @throws
	 */
	public function isExistUpdateData():bool {
		return empty( $this->goDataset()->getUpdateData() );
	}

	/**
	 * 强制save模式
	 * @param bool|null $forceSaveMode [true]强制为create [false]强制为update [null]未强制
	 * @return $this
	 */
	public function forceSaveMode( ?bool $forceSaveMode ):self {
		$this->forceSaveMode = $forceSaveMode;
		//
		return $this;
	}

	/**
	 * 设置主数据id（关联父表）
	 * @param int $mainDataIdOfParent
	 * @return $this
	 */
	public function setMainDataIdOfParent( int $mainDataIdOfParent ):self {
		$this->mainDataIdOfParent = $mainDataIdOfParent;
		return $this;
	}

	/**
	 * 设置主数据id（关联子表）
	 * @param int $mainDataIdOfClient
	 * @return $this
	 */
	public function setMainDataIdOfClient( int $mainDataIdOfClient ):self {
		$this->mainDataIdOfClient = $mainDataIdOfClient;
		return $this;
	}

	/**
	 * 获取关联父表的主数据字段名（关联父表的字段）
	 * @return string
	 */
	public function getMainDataFieldNameRawOfParent():string {
		return $this->relevanceDataFieldMapOfParent[0];
	}

	/**
	 * 获取关联父表的主数据id（关联父表的字段值）
	 * @return int
	 */
	public function getMainDataIdOfParent():int {
		if( $this->mainDataIdOfParent === null ){
			// 从传入的参数中获取
			if( is_array( $this->initParamDataIdOrWhere ) && isset( $this->initParamDataIdOrWhere[ $this->getMainDataFieldNameRawOfParent() ] ) ){
				$this->mainDataIdOfParent = $this->initParamDataIdOrWhere[ $this->getMainDataFieldNameRawOfParent() ];
			} else{
				$this->mainDataIdOfParent = call_user_func( function () {
					// 从where中获取
					if( ! $this->isUpdate() ){
						$whereValue = $this->getWhere( $this->getMainDataFieldNameRawOfParent() );
						if( $whereValue !== null ){
							return $whereValue;
						}
					}
					// 从已设置的数据中获取
					$setData = $this->goDataset()->getSetData( com\Lib\Fun\Str::parseNameAsHump( $this->getMainDataFieldNameRawOfParent(), false ) );
					if( $setData !== null ){
						return $setData;
					}
					// 从数据库中获取
					if( ! $this->isUpdate() ){
						throw com\Lib\Err\Exception::makeOfNew( '非更新模式，无法获取mainDataId', com\Lib\Err\Code::$sysAssertError );
					}
					return $this->goDataset()->getOrFail( $this->getMainDataFieldNameRawOfParent() );
				} );
			}
		}
		return $this->mainDataIdOfParent;
	}

	/**
	 * 获取关联子表的主数据字段名（关联子表的字段）
	 * @return string
	 * @throws
	 */
	public function getMainDataFieldNameRawOfClient():string {
		if( empty( $this->relevanceDataFieldMapOfClient[0] ) ){
			throw com\Lib\Err\Exception::makeOfNew( 'relevanceDataFieldMapOfClient[0]不能为空', com\Lib\Err\Code::$sysDataError );
		}
		return $this->relevanceDataFieldMapOfClient[0];
	}

	/**
	 * 获取关联子表的主数据id（关联子表的字段值）
	 * @return int
	 * @throws
	 */
	public function getMainDataIdOfClient():int {
		if( $this->mainDataIdOfClient === null ){
			// 从传入的参数中获取
			if( is_array( $this->initParamDataIdOrWhere ) && isset( $this->initParamDataIdOrWhere[ $this->getMainDataFieldNameRawOfClient() ] ) ){
				$this->mainDataIdOfClient = $this->initParamDataIdOrWhere[ $this->getMainDataFieldNameRawOfClient() ];
			} else{
				$this->mainDataIdOfClient = call_user_func( function () {
					// 从where中获取
					if( ! $this->isUpdate() ){
						$whereValue = $this->getWhere( $this->getMainDataFieldNameRawOfClient() );
						if( $whereValue !== null ){
							return $whereValue;
						}
					}
					// 从已设置的数据中获取
					$setData = $this->goDataset()->getSetData( com\Lib\Fun\Str::parseNameAsHump( $this->getMainDataFieldNameRawOfClient(), false ) );
					if( $setData !== null ){
						return $setData;
					}
					// 从数据库中获取
					if( ! $this->isUpdate() ){
						throw com\Lib\Err\Exception::makeOfNew( '非更新模式，无法获取mainDataId', com\Lib\Err\Code::$sysAssertError );
					}
					return $this->goDataset()->getOrFail( $this->getMainDataFieldNameRawOfClient() );
				} );
			}
		}
		return $this->mainDataIdOfClient;
	}

	/**
	 * 注入Parent对象
	 * @param MainV3 $parentObj
	 * @param bool $isSave 保存时是否执行InjectParent保存操作 [true][默认]保存 [false]不保存
	 * @return $this
	 */
	public function injectParent( MainV3 $parentObj, bool $isSave = true ):self {
		$this->isInjectParent = true;
		$this->isInjectParentIsSave = $isSave;
		$this->goPoolSet( 'goParent', $parentObj );
		//
		return $this;
	}

	/**
	 * @return MainV3
	 */
	private function __goParent():MainV3 {
		return $this->goParent();
	}

	/**
	 * 是否有注入Parent对象
	 * @return bool
	 */
	protected function isInjectParent():bool {
		return $this->isInjectParent;
	}

	/**
	 * 获取ParentWhere
	 * @return array
	 * @throws
	 */
	protected function getParentWhere():array {
		// ...
		if( $this->getMainDataFieldNameRawOfClient() === null ){
			throw com\Lib\Err\Exception::makeOfNew( '未定义:relevanceDataFieldMapOfClient[0]', com\Lib\Err\Code::$sysDataNotExist );
		}
		if( $this->relevanceDataFieldMapOfParent === null ){
			throw com\Lib\Err\Exception::makeOfNew( '未定义：relevanceDataFieldMapOfParent', com\Lib\Err\Code::$sysDataNotExist );
		}
		//
		if( $this->isCreate() ){
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysAssertError );
		}
		//
		return [
			$this->relevanceDataFieldMapOfParent[1] => $this->getMainDataIdOfParent(),
		];
	}

	/**
	 * 设置数据为where中的eq数据
	 * @return $this
	 */
	public function setDataOfWhere():self {
		$this->goDataset()->sets( com\Lib\Fun\Array_::keyNameToParseNamesAsHump( $this->getEqWhereDatas( true ), false ) );
		//
		return $this;
	}

	/**
	 * 设置数据
	 * @param array $fieldDatas [ '{$fieldName}' => '{$data}', ... ]
	 * @return $this
	 */
	public function sets( array $fieldDatas ):self {
		$this->goDataset()->sets( $fieldDatas );
		//
		return $this;
	}

	public function saveAllReMainId():int {
		return $this->saveAllReThis()->getMainDataIdOfClient();
	}

	/**
	 * 保存数据
	 * @return bool [true][必返]
	 * @throws
	 * @todo :: 临时使用，需求切换到saveAll
	 */
	public function save():bool {
		$this->saveAllReThis();
		//
		return true;
	}

	/**
	 * 保存数据
	 * @return bool [true][必返]
	 * @throws
	 */
	public function saveAll():bool {
		$this->saveAllReThis();
		//
		return true;
	}

	/**
	 * 是否切断Paraen
	 * @var bool
	 */
	private bool $isCutOutParent = false;

	/**
	 * 切断Paraen(创建表时不触发父表创建)
	 * @param bool $isCutOutParent 是否切断Paraen [true][默认]切断
	 * @return $this
	 */
	public function cutOutParent( bool $isCutOutParent = true ):self {
		$this->isCutOutParent = $isCutOutParent;
		//
		return $this;
	}

	/**
	 * 是否存在父表
	 * @return bool
	 */
	public function isExistParent():bool {
		return method_exists( $this, 'goParent' ) || $this->goPoolIsset( 'goParent' );
	}

	/**
	 * 保存数据
	 * @return $this
	 * @throws
	 */
	public function saveAllReThis():self {
		\Hyperf\DbConnection\Db::beginTransaction();
		try{
			// 检查是否存在自定义方法
			if( method_exists( $this, '_saveAll' ) ){
				// 执行自定义方法
				$mainDataId = $this->_saveAll();
			} else{
				if( $this->isCreate() || $this->isExistParent() ){
					if( $this->isCutOutParent ){
						$parentDataId = $this->getMainDataIdOfParent();
					} else{
						// 如果Parent为注入则不创建/修改，防止重复操作数据
						if( ( ! $this->isInjectParent() && $this->methodExists_GoParent() ) || ( $this->isInjectParent() && $this->isInjectParentIsSave ) ){
							if( method_exists( $this, '_saveParent_Before' ) ){
								$this->_saveParent_Before();
							}
							$parentDataId = $this->__goParent()->saveAllReMainId();
						} else if( $this->isInjectParent() && $this->isInjectParentIsSave ){
							$parentDataId = $this->__goParent()->saveAllReMainId();
						}
					}
				}
				// 创建->主表数据
				$selfDataId = $this->saveSelfReId( $parentDataId ?? null );
				$mainDataId = $parentDataId ?? $selfDataId;
				//
				if( method_exists( $this, '_saveAllOfClient_Before' ) ){
					// 创建部分
					$this->_saveAllOfClient_Before( $mainDataId );
				}
				//
				$this->saveAllOfClient( $mainDataId );
			}
			//
			\Hyperf\DbConnection\Db::commit();
			//
			$this->setMainDataIdOfClient( $mainDataId );
			$this->setDataId( $mainDataId );
		} catch( \Exception $e ){
			\Hyperf\DbConnection\Db::rollBack();
			return com\Lib\Err\Exception\Try_::dealWithException( $e, true );
		} finally{
			// 重置数据
			if( $this->saveFinallyResetDatas ){
				$this->resetSetData();
			}
		}
		//
		return $this;
	}

	/**
	 * 保存成功后重置数据
	 * @param bool $isReset
	 * @return $this
	 */
	public function saveFinallyResetDatas( bool $isReset ):self {
		$this->saveFinallyResetDatas = $isReset;
		//
		return $this;
	}

	/**
	 * 保存所有client
	 * @param int $mainDataId
	 * @return bool
	 */
	private function saveAllOfClient( int $mainDataId ):bool {
		$clientsInfoObjs = $this->getAllClientsInfoObj();
		foreach( $clientsInfoObjs as $clientsInfoObj ){
			$clientsInfoObj->saveClient( $mainDataId );
		}
		//
		return true;
	}

	/**
	 * 保存数据(返回数据id)
	 * @return int 返回数据id
	 * @throws
	 */
	public function saveAllReId():int {
		return $this->saveAllReThis()->getDataIdOrFail();
	}

	//	abstract protected function _saveOfSub( int $dataId );

	/**
	 * 保存自身
	 * @param int|null $mainDataId 主数据id [null]创建 [>0]更新
	 * @return int
	 * @throws
	 */
	public function saveSelfReId( int|null $mainDataId = null ):int {
		//
		if( method_exists( $this, '_saveSelf_Before' ) ){
			$this->_saveSelf_Before();
		}
		//
		if( $mainDataId !== null ){
			$currentMainDataField = $this->isExistParent() ? $this->getMainDataFieldNameRawOfParent() : $this->getMainDataFieldNameRawOfClient();
			//
			if( $this->isCreate() ){
				// ...
				/*
				if( empty( $this->getMainDataFieldNameOfClient() ) ){
					throw com\Lib\Err\Exception::makeOfNew( 'relevanceDataFieldMapOfClient[0]不能为空', com\Lib\Err\Code::$sysDataError );
				}
				*/
				$saveData = [
					$currentMainDataField => $mainDataId,
				];
			} else{
				$this->goModelQuery()->where( $currentMainDataField, $mainDataId );
			}
		}
		//
		if( $this->isCreate() ){
			// 创建
			$saveRes = $this->saveOfCreate( $saveData ?? [] );
			//
			return $saveRes->getDataIdOrFail();
		} else{
			// 更新
			$this->saveOfUpdate( [] );
			//
			return $this->getDataIdOrFail();
		}
	}

	/**
	 * 重置所有表的待保存数据
	 * @return $this
	 */
	protected function resetSetData():self {
		// 主表
		$this->goDataset()->reset();
		// 父表
		if( $this->isExistParent() ){
			$this->__goParent()->resetSetData();
		}
		// 子表
		$clientsInfoObjs = $this->getAllClientsInfoObj();
		foreach( $clientsInfoObjs as $clientsInfoObj ){
			$clientsInfoObj->goClient()->resetSetData();
		}
		//
		return $this;
	}

	/**
	 * 检查是否可删除
	 * @return void
	 * @throws
	 */
	private function checkIsDeleteOrFail():void {
		if( $this->isCreate() ){
			throw com\Lib\Err\Exception::makeOfNew( 'Create 模式不可删除', com\Lib\Err\Code::$undefined );
		}
	}

	/**
	 * 删除全部关联数据
	 * @return bool [true][必返]
	 * @throws
	 */
	public function deleteTree():bool {
		$this->checkIsDeleteOrFail();
		//
		\Hyperf\DbConnection\Db::beginTransaction();
		try{
			//
			$this->deleteTreeOfDown( true );
			$this->delete();
			$this->deleteTreeOfParent();
			//
			\Hyperf\DbConnection\Db::commit();
			return true;
		} catch( \Exception $e ){
			\Hyperf\DbConnection\Db::rollBack();
			return com\Lib\Err\Exception\Try_::dealWithException( $e, true );
		}
	}

	/**
	 * 是否存在GoParent方法
	 * @return bool
	 */
	private function methodExists_GoParent():bool {
		return method_exists( $this, 'goParent' );
	}

	/**
	 * 删除上级级关联数据
	 * @return bool [true][必返]
	 * @throws
	 */
	public function deleteTreeOfParent():bool {
		$this->checkIsDeleteOrFail();
		//
		\Hyperf\DbConnection\Db::beginTransaction();
		try{
			//
			if( $this->methodExists_GoParent() ){
				// 删除上级
				$this->__goParent()->delete();
				// 删除上级的上级树
				$this->__goParent()->deleteTreeOfParent();
			}
			//
			\Hyperf\DbConnection\Db::commit();
			return true;
		} catch( \Exception $e ){
			\Hyperf\DbConnection\Db::rollBack();
			return com\Lib\Err\Exception\Try_::dealWithException( $e, true );
		}
	}

	/**
	 * 删除下级关联数据
	 * @param bool $isDelCurrentTable 是否删除当前表 [false][默认]
	 * @return bool [true][必返]
	 * @throws
	 */
	public function deleteTreeOfDown( bool $isDelCurrentTable = false ):bool {
		$this->checkIsDeleteOrFail();
		//
		\Hyperf\DbConnection\Db::beginTransaction();
		try{
			//
			$clientsInfoObjs = $this->getAllClientsInfoObj();
			foreach( $clientsInfoObjs as $clientsInfoObj ){
				$clientsInfoObj->goClient()->deleteTreeOfDown( true );
			}
			//
			if( $isDelCurrentTable === true ){
				$this->delete();
			}
			//
			\Hyperf\DbConnection\Db::commit();
			return true;
		} catch( \Exception $e ){
			\Hyperf\DbConnection\Db::rollBack();
			return com\Lib\Err\Exception\Try_::dealWithException( $e, true );
		}
	}

	/**
	 * 获取类中的所有 goClient 开头的方法名
	 * @return nameMainV3\ClientsInfo[]
	 */
	protected function getAllClientsInfoObj():array {
		$goClientNames = com\Lib\Fun\Class_::getClassMethodsOfMatchPrefix( $this, 'goClient' );
		$clientsInfos = [];
		foreach( $goClientNames as $goClientName ){
			$clientsInfos[] = nameMainV3\ClientsInfo::make( $this, substr( $goClientName, 8 ) );
		}
		return $clientsInfos;
	}

	/**
	 * 获取类中的匹配 $clientDataManagers 的 goClient 开头方法名
	 * @param self[] $clientDataManagers
	 * @return array
	 */
	protected function getAllClientsInfoObjOfMatchDataManager( array $clientDataManagers ) {
		$clientsInfos = $this->getAllClientsInfoObj();
		$matchClientsInfos = [];
		foreach( $clientsInfos as $clientsInfosValue ){
			foreach( $clientDataManagers as $clientDataManagersValue ){
				if( get_class( $clientsInfosValue->goClient() ) === get_class( $clientDataManagersValue ) ){
					$matchClientsInfos[] = $clientsInfosValue;
				}
			}
		}
		// 检查是否完全匹配
		if( count( $clientDataManagers ) !== count( $matchClientsInfos ) ){
			throw com\Lib\Err\Exception::makeOfNew( '部分DataManagers匹配失败', com\Lib\Err\Code::$undefined );
		}
		return $matchClientsInfos;
	}

	/**
	 * 获取类中的所有 saveClient 开头的方法名
	 * @return array
	 */
	protected function getAllSaveClientMethods() {
		return com\Lib\Fun\Class_::getClassMethodsOfMatchPrefix( $this, 'saveClient' );
	}

	/**
	 * 数据完整性修复
	 * @TODO :: 时间关系，暂没提供上级数据的修复功能，目前只能修复所有下级数据
	 * @param self[] $assignDataManagers 仅修复指定的client(并不包含上级)
	 * @return bool [true][必返]
	 */
	public function dataFullFix( ?array $assignDataManagers = null ):bool {
		// 检测自身数据是否存在，不存在则创建
		if( ! $this->isExist() ){
			// 强制创建
			$this->forceSaveMode( true );
			// 创建自己
			$this->saveSelfReId( $this->getMainDataIdOfClient() );
			// 恢复
			$this->forceSaveMode( null );
		}
		//
		$clientsInfos = $assignDataManagers !== null ? $this->getAllClientsInfoObjOfMatchDataManager( $assignDataManagers ) : $this->getAllClientsInfoObj();
		foreach( $clientsInfos as $clientsInfoValue ){
			$clientsInfoValue->goClient()->dataFullFix();
		}
		//
		return true;
	}

	// ===============================
	//
	//
	// 下面为静态方法
	//
	//
	// ===============================

	/**
	 * 数据完整性修复
	 * @TODO :: 时间关系，暂没提供上级数据的修复功能，目前只能修复所有下级数据
	 * @param int $mainDataId 修复指定id的数据
	 * @param self[] $assignDataManagers 仅修复指定的client(并不包含上级)
	 * @return bool [true][必返]
	 */
	public static function dataFullFixOfAll( ?int $mainDataId = null, ?array $assignDataManagers = null ):bool {
		$dataManager = new static();

		if( $mainDataId === null ){
			// 获取所有记录
			$waitFixMainDataIds = $dataManager->goModelQueryOfNew()->pluck( $dataManager->relevanceDataFieldMapOfClient[0] );
		} else{
			$waitFixMainDataIds = [ $mainDataId ];
		}
		// 开始
		$mainDataFieldName = $dataManager->getMainDataFieldNameRawOfClient();
		foreach( $waitFixMainDataIds as $waitFixMainDataId ){
			if( $waitFixMainDataId === 0 ){
				continue;
			}
			//
			( new static( [
				$mainDataFieldName => $waitFixMainDataId,
			] ) )->dataFullFix( $assignDataManagers );
		}
		//
		return true;
	}

}
